package com.fy.client;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import org.neo4j.driver.Record;
import org.neo4j.driver.*;
import org.neo4j.driver.types.Node;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * @ClassName Neo4jLink
 * @author: maliang
 * @Description 查询数据工具类
 * @date 2023/3/31 10:14
 * @Version 1.0版本
 */
@Component
public class Neo4jLink implements AutoCloseable {
    private final Driver driver;

    public Neo4jLink(@Value("${spring.neo4j.uri}") String uri, @Value("${spring.neo4j.authentication.username}") String user, @Value("${spring.neo4j.authentication.password}") String password) {
        driver = GraphDatabase.driver(uri, AuthTokens.basic(user, password));
    }

    @Override
    public void close() throws RuntimeException {
        driver.close();
    }

    public <T> T query(final String cql, Class<T> beanClass) {
        try (Session session = driver.session()) {
            return session.executeWrite(tx -> {
                Query query = new Query(cql);
                Result result = tx.run(query);
                while (result.hasNext()) {
                    Record next = result.next();
                    for (int i = 0; i < next.size(); i++) {
                        Node value = next.get(i).asNode();
                        Map<String, Object> map = value.asMap();
                        T t = BeanUtil.mapToBean(map, beanClass, new CopyOptions());
                        setId(t, value.elementId());
                    }
                }
                return null;
            });
        }
    }

    public <T> T query(final String cql, Map<String, Object> paramMap, Class<T> beanClass) {
        try (Session session = driver.session()) {
            return session.executeWrite(tx -> {
                Query query = new Query(cql, paramMap);
                Result result = tx.run(query);
                while (result.hasNext()) {
                    Record next = result.next();
                    for (int i = 0; i < next.size(); i++) {
                        Node value = next.get(i).asNode();
                        Map<String, Object> map = value.asMap();
                        T t = BeanUtil.mapToBean(map, beanClass, new CopyOptions());
                        setId(t, value.elementId());
                    }
                }
                return null;
            });
        }
    }

    public <T> List<T> queryList(final String cql, Class<T> beanClass) {
        try (Session session = driver.session()) {
            return session.executeWrite(tx -> {
                Query query = new Query(cql);
                Result result = tx.run(query);
                List<T> list = new ArrayList<>();
                while (result.hasNext()) {
                    Record next = result.next();
                    for (int i = 0; i < next.size(); i++) {
                        Node value = next.get(i).asNode();
                        Map<String, Object> map = value.asMap();
                        T t = BeanUtil.mapToBean(map, beanClass, new CopyOptions());
                        setId(t, value.elementId());
                        list.add(t);
                    }
                }
                return list;
            });
        }
    }

    public <T> List<T> queryList(final String cql, Map<String, Object> paramMap, Class<T> beanClass) {
        try (Session session = driver.session()) {
            return session.executeWrite(tx -> {
                Query query = new Query(cql, paramMap);
                Result result = tx.run(query);
                List<T> list = new ArrayList<>();
                while (result.hasNext()) {
                    Record next = result.next();
                    for (int i = 0; i < next.size(); i++) {
                        Node value = next.get(i).asNode();
                        Map<String, Object> map = value.asMap();
                        T t = BeanUtil.mapToBean(map, beanClass, new CopyOptions());
                        setId(t, value.elementId());
                        list.add(t);
                    }
                }
                return list;
            });
        }
    }

    public void write(final String cql) {
        try (Session session = driver.session()) {
            Transaction tx = session.beginTransaction();
            try {
                tx.run(cql);
                tx.commit();
            } catch (Exception e) {
                tx.rollback();
            }
        }
    }

    public void write(final String cql, Map<String, Object> paramMap) {
        try (Session session = driver.session()) {
            Transaction tx = session.beginTransaction();
            try {
                tx.run(cql, paramMap);
                tx.commit();
            } catch (Exception e) {
                tx.rollback();
            }
        }
    }


    public long getCount(String csl) {
        try (Session session = driver.session()) {
            Result run = session.run(csl);
            return run.stream().count();
        }
    }

    public long getCount(String csl, Map<String, Object> paramMap) {
        try (Session session = driver.session()) {
            Result run = session.run(csl, paramMap);
            return run.stream().count();
        }
    }

    private <T> void setId(T t, String primaryKey) {
        Class<?> aClass = t.getClass();
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            if (method.getName().contains("setId")) {
                try {
                    method.invoke(t, Long.parseLong(primaryKey));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                break;
            }
        }
    }

}
